////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2004.
// -------------------------------------------------------------------------
//  File name:   MaterialBrowser.cpp
//  Version:     v1.00
//  Created:     26/8/2004 by Timur.
//  Compilers:   Visual Studio.NET 2003
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "MaterialBrowser.h"
#include "IDataBaseItem.h"
#include "Material.h"
#include "MaterialManager.h"
#include "ViewManager.h"
#include "Objects\BaseObject.h"
#include "Clipboard.h"
#include "NumberDlg.h"
#include "MaterialImageListCtrl.h"
#include "SourceControlDescDlg.h"
#include "crc32.h"

#include <ISourceControl.h>
#include "StringDlg.h"
#include <StringUtils.h>
#include <IThreadTask.h>

#define IDC_MATERIAL_TREECTRL 3

//////////////////////////////////////////////////////////////////////////
#define ITEM_IMAGE_SHARED_MATERIAL   0
#define ITEM_IMAGE_SHARED_MATERIAL_SELECTED 1
#define ITEM_IMAGE_FOLDER            2
#define ITEM_IMAGE_FOLDER_OPEN       3
#define ITEM_IMAGE_MATERIAL          4
#define ITEM_IMAGE_MATERIAL_SELECTED 5
#define ITEM_IMAGE_MULTI_MATERIAL    6
#define ITEM_IMAGE_MULTI_MATERIAL_SELECTED 7


#define ITEM_IMAGE_OVERLAY_CGF          8
#define ITEM_IMAGE_OVERLAY_INPAK        9
#define ITEM_IMAGE_OVERLAY_READONLY     10
#define ITEM_IMAGE_OVERLAY_ONDISK       11
#define ITEM_IMAGE_OVERLAY_LOCKED       12
#define ITEM_IMAGE_OVERLAY_CHECKEDOUT   13
#define ITEM_IMAGE_OVERLAY_NO_CHECKOUT  14
//////////////////////////////////////////////////////////////////////////

enum
{
	MENU_UNDEFINED = 0,
	MENU_CUT,
	MENU_COPY,
	MENU_COPY_NAME,
	MENU_PASTE,
	MENU_EXPLORE,
	MENU_DUPLICATE,
	MENU_RENAME,
	MENU_DELETE,
	MENU_RESET,
	MENU_ASSIGNTOSELECTION,
	MENU_SELECTASSIGNEDOBJECTS,
	MENU_NUM_SUBMTL,
	MENU_ADDNEW,
	MENU_ADDNEW_MULTI,
	MENU_SUBMTL_MAKE,
	MENU_SUBMTL_CLEAR,
	MENU_SAVE_TO_FILE,
	MENU_SAVE_TO_FILE_MULTI,
	MENU_MERGE,

	MENU_SCM_GETPATH,
	MENU_SCM_ADD,
	MENU_SCM_CHECK_IN,
	MENU_SCM_CHECK_OUT,
	MENU_SCM_UNDO_CHECK_OUT,
	MENU_SCM_GET_LATEST,
	MENU_SCM_GET_LATEST_TEXTURES,
	MENU_SCM_HISTORY,
};

///////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////
// Scan materials in a separate thread
//////////////////////////////////////////////////////////////////////////
class CMaterialScanThread : public IThreadTask
{
public:
	static CMaterialScanThread * Instance()
	{
		static CMaterialScanThread  s_instance;
		return &s_instance;
	}
	CMaterialScanThread ()
	{
		m_bNewFiles = false;
		m_bDone = false;
	}
	void StartScan()
	{
		// Kill last scan job and wait until it finishes
		GetISystem()->GetIThreadTaskManager()->UnregisterTask(this);

		// Prepare and start new job
		m_lock.Lock();
		m_filesForUser.clear();
		m_files.clear();
		m_bDone = false;
		m_lock.Unlock();

		SThreadTaskParams params;
		params.nFlags |= THREAD_TASK_BLOCKING;
		params.name = "Material Files Scanning";
		GetISystem()->GetIThreadTaskManager()->RegisterTask(this,params);
	}
	
	bool GetLoadedFiles( CFileUtil::FileArray &files )
	{
		CryAutoCriticalSection lock(m_lock);

		if(m_bNewFiles)
		{
			files = m_filesForUser;
			m_filesForUser.clear();
			m_bNewFiles = false;
			return true;
		}

		return false;
	}

private:
	void AddFiles()
	{
		CryAutoCriticalSection lock(m_lock);

		if (!m_files.empty())
		{
			for (size_t i = 0; i < m_files.size(); i++)
			{
				m_files[i].filename = Path::ToUnixPath( m_files[i].filename );
			}
			m_filesForUser.insert( m_filesForUser.begin(),m_files.begin(),m_files.end() );
			m_files.clear();
			m_bNewFiles = true;
		}
	}

	static bool ScanUpdateCallback( const CString& msg )
	{
		Instance()->AddFiles();
		return true;
	}
	virtual void OnUpdate()
	{
		m_lock.Lock();
		if(m_bDone)
		{
			m_lock.Unlock();
			// Spin until thread gets culled
			Sleep(10);			
			return;
		}
		m_lock.Unlock();

		m_lock.Lock();
		m_files.reserve(5000); // Reserve space for 5000 files.			
		m_lock.Unlock();

		CFileUtil::ScanDirectory( "", "*.mtl", m_files, true, false, &ScanUpdateCallback );
		AddFiles();

		m_lock.Lock();
		m_bDone = true;
		m_lock.Unlock();
	}
	virtual void Stop() {};
	virtual struct SThreadTaskInfo* GetTaskInfo() { return &m_TaskInfo; };

private:
	CryCriticalSection m_lock;
	CFileUtil::FileArray m_filesForUser;
	CFileUtil::FileArray m_files;
	bool m_bNewFiles;
	bool m_bDone;

protected:
	SThreadTaskInfo m_TaskInfo;
};



//////////////////////////////////////////////////////////////////////////
// CMaterialBrowserCtrl
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CMaterialToolBar::CMaterialToolBar() :
	m_pEdit(0),
	m_fIdleFilterTextTime(0.0f)
{
}


//////////////////////////////////////////////////////////////////////////
void CMaterialToolBar::Create(CWnd* pParentWnd)
{
	VERIFY(CreateToolBar(WS_VISIBLE|WS_CHILD|CBRS_TOOLTIPS|CBRS_GRIPPER, pParentWnd, AFX_IDW_TOOLBAR));
	VERIFY(LoadToolBar(IDR_MATERIAL_BROWSER));
	SetFlags(xtpFlagAlignTop|xtpFlagStretched);
	EnableCustomization(FALSE);

	CXTPControl *pCtrl = GetControls()->FindControl(xtpControlButton, ID_MATERIAL_BROWSER_FILTER_TEXT, TRUE, FALSE);
	if (pCtrl)
	{
		int nIndex = pCtrl->GetIndex();
		m_pEdit = (CXTPControlEdit*)GetControls()->SetControlType(nIndex,xtpControlEdit);
		if(m_pEdit)
		{
			m_pEdit->SetFlags(xtpFlagManualUpdate);
			m_pEdit->SetWidth(80);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
BOOL CMaterialToolBar::OnCommand(WPARAM wParam, LPARAM lParam)
{
	if(HIWORD(wParam)==EN_CHANGE)
	{
		m_fIdleFilterTextTime = gEnv->pTimer->GetCurrTime();
	}

	return __super::OnCommand(wParam, lParam);
}


//////////////////////////////////////////////////////////////////////////
bool CMaterialToolBar::GetNewFilterText(CString& outString)
{
	// If user types fast do not proceed for every character. Delay is 0.3 (0.3f) seconds
	if(m_fIdleFilterTextTime>0.0f && gEnv->pTimer->GetCurrTime()-m_fIdleFilterTextTime > 0.3f)
	{
		if(m_pEdit)
		{
			CString filterText = m_pEdit->GetEditText();
			if (m_filterText != filterText)
			{
				m_filterText = filterText;
				outString = m_filterText;
				return true;
			}
		}
		m_fIdleFilterTextTime = 0.0f;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialToolBar::Reset()
{
	if(m_pEdit)
		m_pEdit->SetEditText("");
	m_filterText = "";
	m_fIdleFilterTextTime = 0.0f;
}


//////////////////////////////////////////////////////////////////////////
// CMaterialBrowserCtrl
//////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CMaterialBrowserCtrl, CWnd)
	ON_WM_SIZE()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	ON_COMMAND(ID_MATERIAL_BROWSER_RELOAD, OnReloadItems)
	ON_COMMAND(ID_MATERIAL_BROWSER_CHECKEDOUT, OnShowCheckedOut)
	ON_UPDATE_COMMAND_UI(ID_MATERIAL_BROWSER_CHECKEDOUT, OnUpdateShowCheckedOut)
END_MESSAGE_MAP()


//////////////////////////////////////////////////////////////////////////
CMaterialBrowserCtrl::CMaterialBrowserCtrl() : CToolbarDialog(IDD,NULL)
{
	m_bIgnoreSelectionChange = false;
	m_bItemsValid = true;

	// Load cursors.
	m_hCursorDefault = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
	m_hCursorNoDrop = AfxGetApp()->LoadCursor(IDC_NODROP);
	m_hCursorCreate = AfxGetApp()->LoadCursor(IDC_HIT_CURSOR);
	m_hCursorReplace = AfxGetApp()->LoadCursor(IDC_HAND_INTERNAL);

	m_pMatMan = GetIEditor()->GetMaterialManager();
	m_pMatMan->AddListener(this);
	m_pListener = NULL;

	m_viewType = VIEW_LEVEL;
	m_pMaterialImageListCtrl = NULL;
	m_pLastActiveMultiMaterial = 0;

	m_bNeedReload = false;

	m_bHighlightMaterial = false;
	m_timeOfHighlight = 0;

	m_fIdleSaveMaterialTime = 0.0f;
	m_bShowOnlyCheckedOut = false;

	GetIEditor()->RegisterNotifyListener(this);
}

//////////////////////////////////////////////////////////////////////////
CMaterialBrowserCtrl::~CMaterialBrowserCtrl()
{
	GetIEditor()->UnregisterNotifyListener(this);

	CMaterial *pCurrentMtl = GetCurrentMaterial();
	if (pCurrentMtl != NULL && pCurrentMtl->IsModified())
		pCurrentMtl->Save();

	m_pMaterialImageListCtrl = NULL;
	m_pMatMan->RemoveListener(this);
	ClearItems();

	if (m_bHighlightMaterial)
	{
		m_pMatMan->SetHighlightedMaterial(0);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnSize(UINT nType, int cx, int cy)
{
	__super::OnSize(nType, cx, cy);

	CRect rcClient;
	GetClientRect(rcClient);
	if (!m_tree.m_hWnd)
		return;

	DWORD dwMode = LM_HORZ|LM_HORZDOCK|LM_STRETCH|LM_COMMIT;
	CSize sz = m_wndToolBar.CalcDockingLayout(32000, dwMode);

	CRect rctb = rcClient;
	rctb.bottom = rctb.top + sz.cy;
	m_wndToolBar.MoveWindow(rctb,FALSE);

	rcClient.top = rctb.bottom + 1;

	CRect rctree = rcClient;
	m_tree.MoveWindow(rctree,FALSE);
}

//////////////////////////////////////////////////////////////////////////
BOOL CMaterialBrowserCtrl::Create( const RECT& rect,CWnd* pParentWnd,UINT nID )
{
	BOOL res = CToolbarDialog::Create( IDD,pParentWnd );
	m_wndToolBar.Create(this);

	/*
	LPCSTR lpzClass = AfxRegisterWndClass( CS_VREDRAW|CS_HREDRAW,::LoadCursor(NULL, IDC_ARROW),(HBRUSH)GetStockObject(BLACK_BRUSH),NULL );
	BOOL res = CreateEx( 0,lpzClass,NULL,WS_CHILD|WS_VISIBLE|WS_TABSTOP,rect,pParentWnd,nID );
	VERIFY(res);
	*/

	CRect rc;
	GetClientRect(rc);

	// Create Use materials tree control.
	// Attach it to the control
	GetClientRect(rc);

	//////////////////////////////////////////////////////////////////////////
	m_tree.SetMtlBrowser(this);
	m_tree.Create(WS_CHILD|WS_VISIBLE,CRect(0,0,200,300),this,IDC_MATERIAL_TREECTRL );
	m_tree.EnableAutoNameGrouping(true,ITEM_IMAGE_FOLDER);

	return res;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnReloadItems()
{
	m_wndToolBar.Reset();
	ReloadItems( m_viewType );
}


//////////////////////////////////////////////////////////////////////////
bool CMaterialBrowserCtrl::ShowCheckedOutRecursive(CXTPReportRecords* pRecords)
{
	bool isSomeVisible = false;
	int num = pRecords->GetCount();
	for (int i = 0; i < num; i++)
	{
		CMaterialBrowserRecord* pRecord = (CMaterialBrowserRecord*)pRecords->GetAt(i);
		if(!pRecord || pRecord->nSubMtlSlot>=0) // skip submaterials
			continue;
		bool isChildVisible = ShowCheckedOutRecursive(pRecord->GetChilds());
		bool isVisible = false;
		bool isMaterial = false;
		if(!m_bShowOnlyCheckedOut || isChildVisible)
		{
			isVisible = true;
		}
		else if (!pRecord->materialName.IsEmpty())
		{
			CString filename = m_pMatMan->MaterialToFilename(pRecord->materialName, false);
			CCryFile cryfile;
			if (cryfile.Open( filename, "rb" ))
			{
				if(!cryfile.IsInPak())
				{
					uint32 scFileAttributes = GetIEditor()->GetSourceControl()->GetFileAttributes(filename);
					if((scFileAttributes & SCC_FILE_ATTRIBUTE_MANAGED) && (scFileAttributes & SCC_FILE_ATTRIBUTE_CHECKEDOUT))
					{
						isVisible = true;
						isMaterial = true;
					}
				}
			}
		}
		pRecord->SetVisible(isVisible);

		if(m_bShowOnlyCheckedOut && isVisible)
		{
			if(isMaterial)
			{
				for (int i = 0; i < pRecord->GetChilds()->GetCount(); i++)
					pRecord->GetChilds()->GetAt(i)->SetVisible(TRUE);
			}
			else
			{
				if (!pRecord->IsExpanded())
					pRecord->SetExpanded(TRUE);
			}
		}

		if(isVisible)
			isSomeVisible = true;
	}
	return isSomeVisible;
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnShowCheckedOut()
{
	m_bShowOnlyCheckedOut = !m_bShowOnlyCheckedOut;
	CWaitCursor wait;
	m_wndToolBar.Reset();
	ShowCheckedOutRecursive(m_tree.GetRecords());
	m_tree.Populate();
	m_tree.RedrawControl();
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnUpdateShowCheckedOut(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_bShowOnlyCheckedOut ? 1 : 0);
	pCmdUI->Enable(gSettings.enableSourceControl && GetIEditor()->GetSourceControl() ? TRUE : FALSE);
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::ClearItems()
{
	m_bIgnoreSelectionChange = true;
	m_items.clear();

	m_tree.DeleteAllItems();

	if (m_pMaterialImageListCtrl)
		m_pMaterialImageListCtrl->DeleteAllItems();
	m_pLastActiveMultiMaterial = NULL;
	m_bShowOnlyCheckedOut = false;

	m_bIgnoreSelectionChange = false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::ReloadItems( EViewType viewType )
{
	CWaitCursor wait;

	m_bShowOnlyCheckedOut = false;

	CString prevSelection = GetSelectedMaterialID();

	m_tree.BeginUpdate();

	ClearItems();

	m_viewType = viewType;
	switch (m_viewType) 
	{
	case VIEW_LEVEL:
		ReloadLevelItems();
		break;
	case VIEW_ALL:
	default:
		ReloadAllItems();
		break;
	}

	m_tree.EndUpdate();
	m_tree.Populate();
	m_bItemsValid = true;

	m_bNeedReload = false;

	if (!prevSelection.IsEmpty())
	{
		SelectItemByName( prevSelection );
	}
	else
	{
		SelectItem( m_pMatMan->GetCurrentMaterial() );
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::ReloadLevelItems()
{
	m_bIgnoreSelectionChange = true;

	IDataBaseItem *pSelection = m_pMatMan->GetSelectedItem();
	if (pSelection)
	{
		AddItemToTree( pSelection,false );
	}

	IDataBaseItemEnumerator *pEnum = m_pMatMan->GetItemEnumerator();
	for (IDataBaseItem *pItem = pEnum->GetFirst(); pItem != NULL; pItem = pEnum->GetNext())
	{
		if (pItem != pSelection) // Selected item already added.
		{
			AddItemToTree( pItem,false );
		}
	}
	pEnum->Release();

	m_bIgnoreSelectionChange = false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::AddItemToTree( IDataBaseItem *pItem,bool bUpdate )
{
	if(!pItem)
		return;

	m_bIgnoreSelectionChange = true;
	CMaterialBrowserRecord *pRecord = InsertItemToTree( pItem );
	if (bUpdate)
	{
		//m_tree.UpdateRecord(pRecord,TRUE); // Crashes
	}
	m_bIgnoreSelectionChange = false;
}

//////////////////////////////////////////////////////////////////////////
CMaterialBrowserRecord* CMaterialBrowserCtrl::InsertItemToTree( IDataBaseItem *pItem,int nSubMtlSlot,CMaterialBrowserRecord *pParentRecord )
{
	CMaterial *pMtl = (CMaterial*)pItem;

	CMaterialBrowserRecord *pRecord = new CMaterialBrowserRecord;
	pRecord->materialName = pMtl->GetName();
	pRecord->materialNameCrc32 = MaterialNameToCrc32(pRecord->materialName);
	pRecord->pMaterial = pMtl;
	pRecord->nSubMtlSlot = nSubMtlSlot;
	m_items.insert(pRecord);

	pRecord->CreateItems();
	m_tree.AddTreeRecord( pRecord,pParentRecord );

	if (pMtl->IsMultiSubMaterial())
	{
		ReloadTreeSubMtls(pRecord,false);
	}
	return pRecord;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::ReloadTreeSubMtls( CMaterialBrowserRecord *pRecord,bool bForceCtrlUpdate )
{
	if (pRecord && pRecord->HasChildren())
	{
		for(int i = pRecord->GetChildCount()-1; i>=0; --i)
		{
			CMaterialBrowserRecord* pChildRecord = (CMaterialBrowserRecord*)pRecord->GetChild(i);
			m_items.erase(pChildRecord);
		}
		pRecord->GetChilds()->RemoveAll();
	}

	if (pRecord)
	{
		CMaterial *pMtl = pRecord->pMaterial;
		if (pMtl && pMtl->IsMultiSubMaterial())
		{
			for (int i = 0; i < pMtl->GetSubMaterialCount(); i++)
			{
				CMaterial *pSubMtl = pMtl->GetSubMaterial(i);
				if (pSubMtl)
				{
					InsertItemToTree( pSubMtl,i,pRecord );
				}
			}
			if (bForceCtrlUpdate)
			{
				m_bItemsValid = false;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::RemoveItemFromTree( CMaterialBrowserRecord *pRecord )
{
	// Remove itself from Records
	m_tree.RemoveRecordEx(pRecord);
	m_bItemsValid = false;

	m_items.erase(pRecord);
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::ReloadAllItems()
{
	// First add all items already loaded in the level.
	CMaterialScanThread::Instance()->StartScan();
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::TryLoadRecordMaterial( CMaterialBrowserRecord *pRecord )
{
	if (!pRecord)
		return;

	// If material already loaded ignore.
	if (pRecord->pMaterial || pRecord->bUpdated || pRecord->IsGroup())
		return;

	m_bIgnoreSelectionChange = true;
	pRecord->bUpdated = true;
	// Try to load material for it.
	pRecord->pMaterial = m_pMatMan->LoadMaterial(pRecord->materialName,false);

	m_tree.UpdateItemState( pRecord );

	if (pRecord->pMaterial != NULL && pRecord->pMaterial->IsMultiSubMaterial())
	{
		ReloadTreeSubMtls(pRecord);
	}
	m_bIgnoreSelectionChange = false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::TickRefreshMaterials()
{
	CFileUtil::FileArray files;
	if(CMaterialScanThread::Instance()->GetLoadedFiles( files ))
	{
		LoadFromFiles( files );
	}

	if (!m_bItemsValid)
	{
		PopulateItems();
	}

	const int nMaxUpdatesAtOnce = 1;

	bool bAnyUpdated = false;
	int nNumLoaded = 0;
	//////////////////////////////////////////////////////////////////////////
	//
	//////////////////////////////////////////////////////////////////////////
	CXTPReportRows *pRows = m_tree.GetRows();
	for (int i = 0,num = pRows->GetCount(); i < num; i++)
	{
		CXTPReportRow *pRow = pRows->GetAt(i);
		CMaterialBrowserRecord *pRecord = m_tree.RowToRecord(pRow);
		if (!pRecord->bUpdated && !pRecord->IsGroup() && pRecord->IsVisible())
		{
			if( !pRecord->pMaterial && !pRecord->materialName.IsEmpty())
			{
				TryLoadRecordMaterial(pRecord);
			}
			else
			{
				m_tree.UpdateItemState( pRecord );
				pRecord->bUpdated = true;
			}
			bAnyUpdated = true;
			if (nNumLoaded++ > nMaxUpdatesAtOnce)
				break;
		}
	}
	if (bAnyUpdated)
	{
		m_tree.RedrawControl();
	}

	if (m_bHighlightMaterial)
	{
		uint32 t = GetTickCount();
		if ((t-m_timeOfHighlight) > 300)
		{
			m_bHighlightMaterial = false;
			m_pMatMan->SetHighlightedMaterial(0);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::LoadFromFiles( CFileUtil::FileArray &files )
{
	m_bIgnoreSelectionChange = true;
	m_bShowOnlyCheckedOut = false;

	CMaterialManager *pMatMan = (CMaterialManager*)m_pMatMan;

	m_tree.BeginUpdate();

	CString itemName;
	for (int i = 0; i < files.size(); i++)
	{
		CString name = pMatMan->FilenameToMaterial( files[i].filename );

		CMaterialBrowserRecord *pRecord = new CMaterialBrowserRecord;
		pRecord->materialName = name;
		pRecord->materialNameCrc32 = MaterialNameToCrc32(name);
		m_items.insert(pRecord);

		pRecord->CreateItems();
		m_tree.AddTreeRecord( pRecord,0 );
	}

	m_tree.EndUpdate();

	m_bItemsValid = false;

	m_bIgnoreSelectionChange = false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnEditorNotifyEvent( EEditorNotifyEvent event )
{
	switch (event)
	{
	case eNotify_OnIdleUpdate:
		{
			TickRefreshMaterials();

			CString filterText;
			if(m_wndToolBar.GetNewFilterText(filterText))
			{
				m_bShowOnlyCheckedOut = false;
				m_tree.SetFilterText(filterText);
			}

			// Idle save material
			// If material is changed in less than 1 second (1.0f) it is saved only one time after last change
			if (m_fIdleSaveMaterialTime>0.0001f && gEnv->pTimer->GetCurrTime()-m_fIdleSaveMaterialTime > 1.0f)
			{
				CMaterial* pMtl = GetCurrentMaterial();
				if (pMtl)
					pMtl->Save();
				m_fIdleSaveMaterialTime = 0.0f;
			}
		}
		break;
	case eNotify_OnCloseScene:
		{
			ClearItems();
		}
		break;
	case eNotify_OnEndSceneOpen:
		{
			ReloadItems( m_viewType );
		}
		break;
	}
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::IdleSaveMaterial()
{
	m_fIdleSaveMaterialTime = gEnv->pTimer->GetCurrTime();
}


/*
//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
	if(GetAsyncKeyState( VK_CONTROL ))
	{
		NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
		*pResult = 0;

		HTREEITEM hItem = pNMTreeView->itemNew.hItem;

		CMaterialBrowserRecord *pDraggedItem = (CMaterialBrowserRecord*)pNMTreeView->itemNew.lParam;
		if (!pDraggedItem)
			return;

		CMaterial *pMtl = pDraggedItem->pMaterial;
		if (!pMtl)
		{
			return;
		}

		m_treeCtrl.Select( hItem,TVGN_CARET );

		// Calculate the offset to the hotspot
		CPoint offsetPt(-10,-10);   // Initialize a default offset

		m_hDropItem = 0;
		m_dragImage = m_treeCtrl.CreateDragImage( hItem );
		if (m_dragImage)
		{
			m_hDraggedItem = hItem;
			m_hDropItem = hItem;

			//m_dragImage->BeginDrag(0, CPoint(-10, -10));
			m_dragImage->BeginDrag(0, offsetPt);

			POINT pt = pNMTreeView->ptDrag;
			ClientToScreen( &pt );
			m_dragImage->DragEnter(AfxGetMainWnd(), pt);
			SetCapture();

			GetIEditor()->EnableUpdate( false );
		}
	}

	*pResult = 0;
}
*/

/*
//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_dragImage)
	{
		CPoint p;

		p = point;
		ClientToScreen( &p );
		m_treeCtrl.ScreenToClient( &p );

		SetCursor( m_hCursorDefault );
		TVHITTESTINFO hit;
		ZeroStruct(hit);
		hit.pt = p;
		HTREEITEM hHitItem = m_treeCtrl.HitTest( &hit );
		if (hHitItem)
		{
			if (m_hDropItem != hHitItem)
			{
				if (m_hDropItem)
					m_treeCtrl.SetItem( m_hDropItem,TVIF_STATE,0,0,0,0,TVIS_DROPHILITED,0 );
				// Set state of this item to drop target.
				m_treeCtrl.SetItem( hHitItem,TVIF_STATE,0,0,0,TVIS_DROPHILITED,TVIS_DROPHILITED,0 );
				m_hDropItem = hHitItem;
				//m_treeCtrl.Invalidate();
			}
		}

		// Check if this is not SubMaterial.
		CMaterialBrowserRecord *pRecord = GetItemInfo(m_hDraggedItem);
		CMaterial *pMtl = GetMaterial(m_hDraggedItem);
		if (!pMtl || pRecord->nSubMtlSlot >= 0)
		{
			SetCursor( m_hCursorNoDrop );
		}
		else
		{
			CRect rc;
			AfxGetMainWnd()->GetWindowRect( rc );
			p = point;
			ClientToScreen( &p );
			p.x -= rc.left;
			p.y -= rc.top;
			m_dragImage->DragMove( p );

			// Check if can drop here.
			{
				CPoint p;
				GetCursorPos( &p );
				CViewport* viewport = GetIEditor()->GetViewManager()->GetViewportAtPoint( p );
				if (viewport)
				{
					CPoint vp = p;
					viewport->ScreenToClient(&vp);
					HitContext hit;
					if (viewport->HitTest( vp,hit ))
					{
						if (hit.object)
						{
							SetCursor( m_hCursorReplace );
						}
					}
					else
					{
						if (viewport->CanDrop(vp,pMtl))
							SetCursor( m_hCursorReplace );
					}
				}
			}
		}
	}

	__super::OnMouseMove(nFlags, point);
}
*/

/*
//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
	//CXTResizeDialog::OnLButtonUp(nFlags, point);

	if (m_hDropItem)
	{
		m_treeCtrl.SetItem( m_hDropItem,TVIF_STATE,0,0,0,0,TVIS_DROPHILITED,0 );
		m_hDropItem = 0;
	}

	if (m_dragImage)
	{
		CPoint p;
		GetCursorPos( &p );

		GetIEditor()->EnableUpdate( true );

		m_dragImage->DragLeave( AfxGetMainWnd() );
		m_dragImage->EndDrag();
		delete m_dragImage;
		m_dragImage = 0;
		ReleaseCapture();

		CPoint treepoint = p;
		m_treeCtrl.ScreenToClient( &treepoint );

		TVHITTESTINFO hit;
		ZeroStruct(hit);
		hit.pt = treepoint;
		HTREEITEM hHitItem = m_treeCtrl.HitTest( &hit );
		if (hHitItem)
		{
			DropToItem( hHitItem,m_hDraggedItem );
			m_hDraggedItem = 0;
			return;
		}

		// Check if this is not SubMaterial.
		CMaterialBrowserRecord *pRecord = GetItemInfo(m_hDraggedItem);
		if (pRecord->nSubMtlSlot < 0)
		{
			CWnd *wnd = WindowFromPoint( p );

			CUndo undo( "Assign Material" );

			CViewport* viewport = GetIEditor()->GetViewManager()->GetViewportAtPoint( p );
			if (viewport)
			{
				CPoint vp = p;
				viewport->ScreenToClient(&vp);
				// Drag and drop into one of views.
				// Start object creation.
				HitContext hit;
				if (viewport->HitTest( vp,hit ))
				{
					if (hit.object)
					{
						CMaterial *pMtl = GetMaterial(m_hDraggedItem);
						if (pMtl && !pMtl->IsPureChild())
						{
							hit.object->SetMaterial(pMtl);
						}
					}
				}
				else
				{
					CMaterial *pMtl = GetMaterial(m_hDraggedItem);
					viewport->Drop(vp,pMtl);
				}
			}
			else
			{
			}
		}
	}
	m_hDraggedItem = 0;

	__super::OnLButtonUp(nFlags, point);
}
*/


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::DropToItem( CMaterialBrowserRecord *pTrgItem,CMaterialBrowserRecord *pSrcItem )
{
	if (pTrgItem && pSrcItem)
	{
		// Get multisub parent.
		CMaterialBrowserRecord *pTrgParentItem = pTrgItem->GetParent();
		CMaterialBrowserRecord *pSrcParentItem = pSrcItem->GetParent();

		_smart_ptr<CMaterial> pTrgMtl = pTrgItem->pMaterial;
		_smart_ptr<CMaterial> pSrcMtl = pSrcItem->pMaterial;
		if (pSrcMtl != NULL && !pSrcMtl->IsMultiSubMaterial())
		{
			if (pTrgParentItem == pSrcParentItem)
			{
				// We are possibly dropping within same multi sub object.
				if (pTrgItem->nSubMtlSlot >= 0 && pSrcItem->nSubMtlSlot >= 0)
				{
					if (pTrgParentItem && pTrgParentItem->pMaterial != NULL && pTrgParentItem->pMaterial->IsMultiSubMaterial())
					{
						// Swap materials.
						_smart_ptr<CMaterial> pParentMultiMaterial = pTrgParentItem->pMaterial;
						int nSrcSlot = pSrcItem->nSubMtlSlot;
						int nTrgSlot = pTrgItem->nSubMtlSlot;
						// This invalidates item infos.
						SetSubMaterial( pTrgParentItem,nSrcSlot,pTrgMtl,false );
						SetSubMaterial( pTrgParentItem,nTrgSlot,pSrcMtl,true );
						return;
					}
				}
			}
			else if (!pSrcMtl->IsPureChild())
			{
				// Not in same branch.
				if (pTrgItem->nSubMtlSlot >= 0)
				{
					if (pTrgParentItem && pTrgParentItem->pMaterial != NULL && pTrgParentItem->pMaterial->IsMultiSubMaterial())
					{
						SetSubMaterial( pTrgParentItem,pTrgItem->nSubMtlSlot,pSrcMtl,true );
						return;
					}
				}
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::SetSelectedItem( CMaterialBrowserRecord *pRecord,const TMaterialBrowserRecords *pMarkedRecords,bool bFromOnSelectOfCtrl )
{
	if (m_bIgnoreSelectionChange)
		return;

	m_bIgnoreSelectionChange = true;
	if (pMarkedRecords)
	{
		m_markedRecords = *pMarkedRecords;
	}
	else
	{
		m_markedRecords.clear();
	}

	CMaterial* pMtl = NULL;
	if (pRecord && !pRecord->IsGroup())
	{
		TryLoadRecordMaterial(pRecord);
		pMtl = pRecord->pMaterial;
	}

	if (!bFromOnSelectOfCtrl)
	{
		m_tree.SelectItem( pRecord,pMarkedRecords );
	}

	RefreshSelected();

	if (m_pListener)
		m_pListener->OnBrowserSelectItem(pMtl,false);

	m_timeOfHighlight = GetTickCount();
	m_pMatMan->SetHighlightedMaterial(pMtl);
	if (pMtl)
		m_bHighlightMaterial = true;

	std::vector<_smart_ptr<CMaterial> > markedMaterials;
	if (pMarkedRecords)
	{
		for (size_t i = 0; i < pMarkedRecords->size(); ++i)
		{
			markedMaterials.push_back( (*pMarkedRecords)[i]->pMaterial );
		}
	}
	m_pMatMan->SetMarkedMaterials( markedMaterials );

	m_bIgnoreSelectionChange = false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::SelectItem( IDataBaseItem *pItem )
{
	if (m_bIgnoreSelectionChange)
		return;

	if(!pItem)
		return;

	bool bFound = false;
	for (Items::iterator it = m_items.begin(); it != m_items.end(); ++it)
	{
		CMaterialBrowserRecord *pRecord = *it;
		if (pRecord->pMaterial == pItem)
		{
			SetSelectedItem( pRecord,0,false );
			bFound = true;
			break;
		}
	}

	if (!bFound && pItem && pItem->GetType() == EDB_TYPE_MATERIAL)
	{
		bool bLookForParent = false;
		CString mtlName = pItem->GetName();
		CMaterial *pMtl = (CMaterial*)pItem;
		if (pMtl->GetParent())
		{
			mtlName = pMtl->GetParent()->GetName();
			bLookForParent = true;
		}

		// If material not found, try finding by name
		if (SelectItemByName( mtlName ))
		{
			bFound = true;
			if (bLookForParent)
			{
				// If we found parent, it must have been loaded and we can try again finding the actual record with a material.
				for (Items::iterator it = m_items.begin(); it != m_items.end(); ++it)
				{
					CMaterialBrowserRecord *pRecord = *it;
					if (pRecord->pMaterial == pItem)
					{
						SetSelectedItem( pRecord,0,false );
						break;
					}
				}
			}
		}
	}
	if (!bFound)
	{
		SetSelectedItem( 0,0,false );
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnDuplicate()
{
	GetIEditor()->ExecuteCommand( "Material.Duplicate" );
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnCut()
{
	CMaterialBrowserRecord *pRecord = GetSelectedRecord();
	if (pRecord)
	{
		OnCopy();
		DeleteItem(pRecord);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnCopyName()
{
	CMaterial *pMtl = GetCurrentMaterial();
	if (pMtl)
	{
		CClipboard clipboard;
		clipboard.PutString( pMtl->GetName(),"Material Name" );
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnCopy()
{
	CMaterial *pMtl = GetCurrentMaterial();
	if (pMtl)
	{
		CClipboard clipboard;
		XmlNodeRef node = XmlHelpers::CreateXmlNode( "Material" );
		node->setAttr("Name",pMtl->GetName());
		CBaseLibraryItem::SerializeContext ctx( node,false );
		ctx.bCopyPaste = true;
		pMtl->Serialize( ctx );
		clipboard.Put( node );
	}
}

//////////////////////////////////////////////////////////////////////////
bool CMaterialBrowserCtrl::CanPaste()
{
	CClipboard clipboard;
	if (clipboard.IsEmpty())
		return false;
	XmlNodeRef node = clipboard.Get();
	if (!node)
		return false;

	if (strcmp(node->getTag(),"Material") == 0)
	{
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnPaste()
{
	CClipboard clipboard;
	if (clipboard.IsEmpty())
		return;
	XmlNodeRef node = clipboard.Get();
	if (!node)
		return;

	if (strcmp(node->getTag(),"Material") == 0)
	{
		GetIEditor()->ExecuteCommand( "Material.Create" );
		CMaterial *pMtl = m_pMatMan->GetCurrentMaterial();
		if (pMtl)
		{
			// This is material node.
			CBaseLibraryItem::SerializeContext serCtx( node,true );
			serCtx.bCopyPaste = true;
			serCtx.bUniqName = true;
			pMtl->Serialize( serCtx );

			SelectItem( pMtl );
			pMtl->Save();
			pMtl->Reload();
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnAddNewMaterial()
{
	GetIEditor()->ExecuteCommand( "Material.Create" );
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnAddNewMultiMaterial()
{
	GetIEditor()->ExecuteCommand( "Material.CreateMulti" );
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnMergeMaterials()
{
	GetIEditor()->ExecuteCommand( "Material.Merge" );
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::DeleteItem()
{
	CMaterialBrowserRecord *pRecord = GetSelectedRecord();
	if (pRecord)
	{
		DeleteItem(pRecord);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnResetItem()
{
	if (MessageBox(_T("Reset Material to defaults?"),_T("Reset Material"),MB_YESNO|MB_ICONQUESTION) == IDYES)
	{
		CMaterial *pMtl = GetCurrentMaterial();
		int index;

		pMtl->GetSubMaterialCount()>0?index=pMtl->GetSubMaterialCount():index=1;

		if(pMtl)
		{
			for(int i=0; i<index; i++)
			{
				pMtl->GetSubMaterialCount()>0?pMtl->GetSubMaterial(i)->Reload():pMtl->Reload();
				TickRefreshMaterials();
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::DeleteItem( CMaterialBrowserRecord *pRecord )
{
	if (pRecord)
	{
		CMaterial *pMtl = pRecord->pMaterial;
		if(pMtl)
		{
			CSourceControlDescDlg dlg;
			if(!(pMtl->GetFileAttributes() & SCC_FILE_ATTRIBUTE_MANAGED) || dlg.DoModal()==IDOK)
			{
				if (pMtl->GetFileAttributes() & SCC_FILE_ATTRIBUTE_MANAGED)
				{
					if(!GetIEditor()->GetSourceControl()->Delete(pMtl->GetFilename(), dlg.m_sDesc))
					{
						MessageBox("Could not delete file from source control!", "Error", MB_OK | MB_ICONERROR);
					}
				}
				if (pRecord->nSubMtlSlot!=-1)
					OnClearSubMtlSlot(pRecord);
				else
					GetIEditor()->ExecuteCommand("Material.Delete");
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnRenameItem()
{
	CMaterial *pMtl = GetCurrentMaterial();
	if (!pMtl)
		return;

	if (pMtl->IsPureChild())
	{
		CStringDlg dlg("Enter New Material Name",this);
		dlg.SetString( pMtl->GetName() );
		if (dlg.DoModal() == IDOK)
		{
			pMtl->SetName( dlg.GetString() );
			pMtl->Save();
			pMtl->Reload();
		}
	}
	else
	{
		if( (pMtl->GetFileAttributes() & SCC_FILE_ATTRIBUTE_MANAGED) &&
			!(pMtl->GetFileAttributes() & SCC_FILE_ATTRIBUTE_CHECKEDOUT) )
		{
			if( IDCANCEL == MessageBox("Only checked-out files can be renamed. Check out and mark for delete before rename it?", "Confirm", MB_OKCANCEL) )
				return;			
		}

		CString startPath = GetIEditor()->GetSearchPath(EDITOR_PATH_MATERIALS);
		if (pMtl)
			startPath = Path::GetPath( pMtl->GetFilename() );

		CString filename;
		if (!CFileUtil::SelectSaveFile( "Material Files (*.mtl)|*.mtl","mtl",startPath,filename ))
		{
			return;
		}
		CString itemName = m_pMatMan->FilenameToMaterial( Path::GetRelativePath(filename) );
		if (itemName.IsEmpty())
			return;

		if (m_pMatMan->FindItemByName( itemName ))
		{
			Warning( "Material with name %s already exist",(const char*)itemName );
			return;
		}

		if (pMtl && (pMtl->GetFileAttributes() & SCC_FILE_ATTRIBUTE_MANAGED) )
		{			
			if(pMtl->GetFileAttributes() & SCC_FILE_ATTRIBUTE_CHECKEDOUT)
			{
				if( IDCANCEL == MessageBox("The original file will be marked for delete and the new named file will be marked for integration.", "Confirm", MB_OKCANCEL) )			
					return;
			}
			else
				GetIEditor()->GetSourceControl()->CheckOut(pMtl->GetFilename());

			if(!GetIEditor()->GetSourceControl()->Rename( pMtl->GetFilename(),filename, "Rename" ))
			{
				MessageBox("Could not rename file in Source Control.", "Error", MB_OK | MB_ICONERROR);
			}
		}
			

		// Delete file on disk.
		if (!pMtl->GetFilename().IsEmpty())
		{
			::DeleteFile( pMtl->GetFilename() );
		}
		pMtl->SetName( itemName );
		pMtl->Save();

		//OnDataBaseItemEvent( pMtl,EDB_ITEM_EVENT_DELETE );
		//OnDataBaseItemEvent( pMtl,EDB_ITEM_EVENT_ADD );
	}

	/*
	CMaterial * pMat = GetIEditor()->GetMaterialManager()->GetCurrentMaterial();
	if(pMat)
	{
		if(!GetIEditor()->GetSourceControl()->Rename(pMat->GetFilename(), "Newname"))
		{
			MessageBox("Operation could not be completed.", "Error", MB_OK | MB_ICONERROR);
		}
	}
	*/
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnSetSubMtlCount( CMaterialBrowserRecord *pRecord )
{
	CMaterial *pMtl = pRecord->pMaterial;
	if (!pMtl)
		return;

	if (!pMtl->IsMultiSubMaterial())
		return;

	int num = pMtl->GetSubMaterialCount();
	CNumberDlg dlg(0,num,"Number of Sub Materials");
	dlg.SetRange(0.0, (float) MAX_SUB_MATERIALS);
	dlg.SetInteger(true);
	if (dlg.DoModal() == IDOK)
	{
		num = dlg.GetValue();
		if (num != pMtl->GetSubMaterialCount())
		{
			CUndo undo( "Set SubMtl Count" );
			pMtl->SetSubMaterialCount(num);

			for (int i = 0; i < num; i++)
			{
				if (pMtl->GetSubMaterial(i) == 0)
				{
					// Allocate pure childs for all empty slots.
					CString name;
					name.Format("SubMtl%d",i+1);
					_smart_ptr<CMaterial> pChild = new CMaterial( m_pMatMan,name,MTL_FLAG_PURE_CHILD );
					pMtl->SetSubMaterial(i,pChild);
				}
			}
		}
	}
}


//////////////////////////////////////////////////////////////////////////
int CMaterialBrowserCtrl::GetTexturesFromMaterial(CMaterial * pMtl, CString & outPath)
{
	int cnt = 0;
	if(pMtl)
	{
		SInputShaderResources &sr = pMtl->GetShaderResources();
		for (int i = 0; i < EFTT_MAX; i++)
		{
			if(strlen(sr.m_Textures[i].m_Name)>0)
			{
				CString name = sr.m_Textures[i].m_Name;
				CString ext = name.Right(4);
				if(!stricmp(ext, ".tif"))
				{
					CString path = Path::GamePathToFullPath(name);
					if(!path.IsEmpty())
					{
						if(cnt)
							outPath.Append(";");
						outPath.Append(path);
						cnt++;
					}
				}
			}
		}
		if(pMtl->IsMultiSubMaterial())
		{
			for(int i=0; i<pMtl->GetSubMaterialCount(); i++)
			{
				CMaterial * pSubMtl = pMtl->GetSubMaterial(i);
				if(pSubMtl)
				{
					SInputShaderResources &sr = pSubMtl->GetShaderResources();
					for (int i = 0; i < EFTT_MAX; i++)
					{
						if(strlen(sr.m_Textures[i].m_Name)>0)
						{
							CString name = sr.m_Textures[i].m_Name;
							CString ext = name.Right(4);
							if(!stricmp(ext, ".tif"))
							{
								CString path = Path::GamePathToFullPath(name);
								if(!path.IsEmpty())
								{
									if(cnt)
										outPath.Append(";");
									outPath.Append(path);
									cnt++;
								}

								CString dccFilename;
								if ( CFileUtil::CalculateDccFilename(name,dccFilename) )
								{
									path = Path::GamePathToFullPath(dccFilename);
									if(!path.IsEmpty())
									{
										if(cnt)
											outPath.Append(";");
										outPath.Append(path);
										cnt++;
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return cnt;
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::DoSourceControlOp( CMaterialBrowserRecord *pRecord,ESourceControlOp scmOp )
{
	if (!pRecord)
		return;

	CMaterial *pMtl = NULL;

	if (pRecord)
		pMtl = pRecord->pMaterial;

	if(pMtl && pMtl->IsPureChild())
		pMtl = pMtl->GetParent();

	CString path;
	if (pMtl)
		path = pMtl->GetFilename();
	else
		path = pRecord->CollectChildItems();

	if(path.IsEmpty())
		return;

	if (pMtl && pMtl->IsModified())
		pMtl->Save();

	// Collecting texture pathes
	CString multipath;
	if(pMtl)
		GetTexturesFromMaterial(pMtl, multipath);

	bool bRes = true;
	switch (scmOp)
	{
	case ESCM_GETPATH:
		{
			char outPath[MAX_PATH];
			bRes = GetIEditor()->GetSourceControl()->GetInternalPath( path, outPath, MAX_PATH);
			if(bRes && *outPath)
			{
				CClipboard clipboard;
				clipboard.PutString( outPath );
				return; // do not need to reload the material
			}
		}
		break;
	case ESCM_IMPORT:
		{
			CSourceControlDescDlg dlg;
			if(dlg.DoModal()==IDOK)
			{
				if(!multipath.IsEmpty())
					GetIEditor()->GetSourceControl()->Add( multipath, dlg.m_sDesc );
				bRes = GetIEditor()->GetSourceControl()->Add( path, dlg.m_sDesc );
			}
			else
				bRes=true;
		}
		break;
	case ESCM_CHECKIN:
		{
			CSourceControlDescDlg dlg;
			if(dlg.DoModal()==IDOK)
			{
				if(!multipath.IsEmpty())
				{
					GetIEditor()->GetSourceControl()->Add( multipath, dlg.m_sDesc );
					GetIEditor()->GetSourceControl()->CheckIn( multipath, dlg.m_sDesc );
				}
				bRes = GetIEditor()->GetSourceControl()->CheckIn( path, dlg.m_sDesc );
			}
			else
				bRes=true;
		}
		break;
	case ESCM_CHECKOUT:
		{
			if (pMtl && (pMtl->GetFileAttributes() & SCC_FILE_ATTRIBUTE_BYANOTHER))
			{
				char username[64];
				if(!GetIEditor()->GetSourceControl()->GetOtherUser(pMtl->GetFilename(), username, 64))
					strcpy(username, "another user");
				CString str;
				str.Format( _T("This file was checked out by %s. Try to continue?"), username);
				if( AfxMessageBox( str, MB_YESNO|MB_ICONQUESTION ) != IDYES)
					return;
			}
			if(bRes = GetIEditor()->GetSourceControl()->GetLatestVersion( path ))
			{
				bRes = GetIEditor()->GetSourceControl()->CheckOut( path );
			}
		}
		break;
	case ESCM_UNDO_CHECKOUT:
		bRes = GetIEditor()->GetSourceControl()->UndoCheckOut( path );
		break;
	case ESCM_GETLATEST:
		bRes = GetIEditor()->GetSourceControl()->GetLatestVersion( path );
		break;
	case ESCM_GETLATESTTEXTURES:
		if(!multipath.IsEmpty())
			bRes = GetIEditor()->GetSourceControl()->GetLatestVersion( multipath );
		break;
	case ESCM_HISTORY:
		bRes = GetIEditor()->GetSourceControl()->History( path );
		if (bRes)
			return;
		break;
	}

	if (bRes)
	{
		if (pRecord)
		{
			if(pMtl)
			{
				pMtl->Reload();
			}
			else
			{
				pRecord->UpdateChildItems();
			}
		}
	}
	else
	{
		MessageBox("Source Control Operation Failed.\r\nCheck if Source Control Provider correctly setup and working directory is correct.", "Error", MB_OK | MB_ICONERROR);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnMakeSubMtlSlot( CMaterialBrowserRecord *pRecord )
{
	if (pRecord && pRecord->nSubMtlSlot >= 0)
	{
		CMaterialBrowserRecord *pParentInfo = pRecord->GetParent();
		if (pParentInfo && pParentInfo->pMaterial != NULL && pParentInfo->pMaterial->IsMultiSubMaterial())
		{
			CString str;
			str.Format( _T("Making new material will override material currently assigned to the slot %d of %s\r\nMake new sub material?"),
				pRecord->nSubMtlSlot,pParentInfo->pMaterial->GetName() );
			if (MessageBox(str,_T("Confirm Override"),MB_YESNO|MB_ICONQUESTION) == IDYES)
			{
				CString name;
				name.Format("SubMtl%d",pRecord->nSubMtlSlot+1);
				_smart_ptr<CMaterial> pMtl = new CMaterial( m_pMatMan,name,MTL_FLAG_PURE_CHILD );
				pParentInfo->pMaterial->SetSubMaterial(pRecord->nSubMtlSlot,pMtl);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnClearSubMtlSlot( CMaterialBrowserRecord *pRecord )
{
	if (pRecord && pRecord->nSubMtlSlot >= 0 && pRecord->pMaterial != NULL)
	{
		CMaterialBrowserRecord *pParentInfo = pRecord->GetParent();
		if (pParentInfo && pParentInfo->pMaterial != NULL && pParentInfo->pMaterial->IsMultiSubMaterial())
		{
			CString str;
			str.Format( _T("Clear Sub-Material Slot %d of %s?"),pRecord->nSubMtlSlot,pParentInfo->pMaterial->GetName() );
			if (MessageBox(str,_T("Clear Sub-Material"),MB_YESNO|MB_ICONQUESTION) == IDYES)
			{
				CUndo undo("Material Change");
				SetSubMaterial( pParentInfo,pRecord->nSubMtlSlot,0,true );
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::SetSubMaterial( CMaterialBrowserRecord *pRecord,int nSlot,CMaterial *pSubMaterial,bool bSelectSubMtl )
{
	if (pRecord && pRecord->pMaterial != NULL && pRecord->pMaterial->IsMultiSubMaterial())
	{
		_smart_ptr<CMaterial> pMultiMat = pRecord->pMaterial;
		pMultiMat->SetSubMaterial(nSlot,pSubMaterial);
	}
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnDataBaseItemEvent( IDataBaseItem *pItem,EDataBaseItemEvent event )
{
	if (m_bIgnoreSelectionChange)
		return;

	if(!pItem)
		return;

	CMaterial *pMtl = (CMaterial*)pItem;

	switch(event)
	{
	case EDB_ITEM_EVENT_ADD:
		if (!pMtl->IsPureChild())
		{
			AddItemToTree(pMtl,false);
			m_bItemsValid = false;
		}
		break;
	case EDB_ITEM_EVENT_DELETE:
		if (!pMtl->IsPureChild())
		{
			Items::iterator next;
			for (Items::iterator it = m_items.begin(); it != m_items.end(); it = next)
			{
				next = it; next++;
				CMaterialBrowserRecord *pRecord = *it;
				if (pRecord->pMaterial == pItem)
				{
					RemoveItemFromTree(pRecord);
					m_bItemsValid = false;
					break;
				}
			}
		}
		break;
	case EDB_ITEM_EVENT_CHANGED:
		{
			CMaterialBrowserRecord* pRecord = 0;
			for (Items::iterator ppRecord = m_items.begin(); ppRecord != m_items.end(); ++ppRecord)
			{
				if ((*ppRecord)->pMaterial == pMtl)
				{
					pRecord = *ppRecord;
					break;
				}
			}

			if(pRecord)
			{
				pRecord->SetName( pMtl->GetShortName() );
				pRecord->materialName = pMtl->GetName();
				pRecord->materialNameCrc32 = MaterialNameToCrc32(pRecord->materialName);


				if (pMtl->IsMultiSubMaterial())
				{
					ReloadTreeSubMtls(pRecord);
				}

				m_tree.UpdateItemState( pRecord );
			}

			if (pItem == GetCurrentMaterial())
			{
				if (pMtl->IsMultiSubMaterial())
					m_pLastActiveMultiMaterial = NULL;
				RefreshSelected();
			}
		}
		m_bItemsValid = false;
		break;
	case EDB_ITEM_EVENT_SELECTED:
		{
			SelectItem(pItem);
		}
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::SetImageListCtrl( CMaterialImageListCtrl *pCtrl )
{
	m_pMaterialImageListCtrl = pCtrl;
	if (m_pMaterialImageListCtrl)
		m_pMaterialImageListCtrl->SetSelectMaterialCallback( functor(*this,&CMaterialBrowserCtrl::OnImageListCtrlSelect) );
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnImageListCtrlSelect( CImageListCtrlItem *pMtlItem )
{
	int nSlot = (INT_PTR)pMtlItem->pUserData;
	if (nSlot < 0)
	{
		return;
	}

	CMaterialBrowserRecord *pRecord = GetSelectedRecord();
	if (!pRecord)
		return;

	CMaterialBrowserRecord *pParentInfo = 0;
	if (pRecord->nSubMtlSlot < 0)
	{
		// Current selected material is not a sub slot.
		pParentInfo = pRecord;
	}
	else
	{
		// Current selected material is a sub slot.
		pParentInfo = pRecord->GetParent();
	}

	if (!pParentInfo)
		return;

	if (pParentInfo->pMaterial == NULL || !pParentInfo->pMaterial->IsMultiSubMaterial())
		return; // must be multi sub material.

	if (nSlot >= pParentInfo->pMaterial->GetSubMaterialCount())
		return;

	CMaterial *pMtl = pParentInfo->pMaterial->GetSubMaterial(nSlot);
	if (pMtl)
	{
		SelectItem(pMtl);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::OnSaveToFile( bool bMulti )
{
	CMaterial *pCurrentMaterial = GetCurrentMaterial();
	if (pCurrentMaterial)	
	{
		CString startPath = GetIEditor()->GetSearchPath(EDITOR_PATH_MATERIALS);
		CString filename;
		if (!CFileUtil::SelectSaveFile( "Material Files (*.mtl)|*.mtl","mtl",startPath,filename ))
		{
			return;
		}
		filename = Path::GetRelativePath(filename);
		CString itemName = Path::RemoveExtension(filename);
		itemName = Path::MakeGamePath(itemName);

		if (m_pMatMan->FindItemByName( itemName ))
		{
			Warning( "Material with name %s already exist",(const char*)itemName );
			return;
		}
		int flags = pCurrentMaterial->GetFlags();
		if (bMulti)
			flags |= MTL_FLAG_MULTI_SUBMTL;

		pCurrentMaterial->SetFlags( flags );

		if (pCurrentMaterial->IsDummy())
		{
			pCurrentMaterial->ClearMatInfo();
			pCurrentMaterial->SetDummy( false );
		}
		pCurrentMaterial->SetModified(true);
		pCurrentMaterial->Save();
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::RefreshSelected()
{
	CMaterialBrowserRecord *pRecord = GetSelectedRecord();
	if (!pRecord)
		return;

	CMaterial *pMtl = NULL;

	TryLoadRecordMaterial(pRecord);

	pMtl = pRecord->pMaterial;

	if (m_pMaterialImageListCtrl)
	{
		m_pMaterialImageListCtrl->InvalidateMaterial(pMtl);
		if (pMtl)
		{
			CMaterial *pMultiMtl = 0;
			if (pMtl->IsMultiSubMaterial())
			{
				pMultiMtl = pMtl;
			}
			else if (pRecord->nSubMtlSlot >= 0)
			{
				CMaterialBrowserRecord *pParentInfo = pRecord->GetParent();
				if (pParentInfo)
				{
					pMultiMtl = pParentInfo->pMaterial;
					assert( pMultiMtl );
				}
			}

			if (m_pLastActiveMultiMaterial != pMultiMtl || pMultiMtl == 0)
			{
				// Normal material.
				m_pMaterialImageListCtrl->DeleteAllItems();
				m_pMaterialImageListCtrl->AddMaterial( pMtl,(void*)pRecord->nSubMtlSlot );
			}
			else
			{
				m_pMaterialImageListCtrl->SetMaterial( 0,pMtl,(void*)pRecord->nSubMtlSlot );
			}
			if (m_pLastActiveMultiMaterial != pMultiMtl && pMultiMtl != 0)
			{
				for (int i = 0; i < pMultiMtl->GetSubMaterialCount(); i++)
				{
					if (pMultiMtl->GetSubMaterial(i))
						m_pMaterialImageListCtrl->AddMaterial( pMultiMtl->GetSubMaterial(i),(void*)i );
				}
				m_pMaterialImageListCtrl->ResetSelection();
				if (pRecord->nSubMtlSlot >= 0)
				{
					m_pMaterialImageListCtrl->SelectItem( pRecord->nSubMtlSlot+1 );
				}
			}
			m_pMaterialImageListCtrl->SelectMaterial( pMtl );
			m_pLastActiveMultiMaterial = pMultiMtl;
		}
		else
		{
			m_pMaterialImageListCtrl->DeleteAllItems();
			m_pLastActiveMultiMaterial = NULL;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::ShowContextMenu( CMaterialBrowserRecord *pRecord,CPoint point )
{
	_smart_ptr<CMaterial> pMtl;
	if (pRecord)
	{
		pMtl = pRecord->pMaterial;
	}

	// Create pop up menu.
	CMenu menu;
	menu.CreatePopupMenu();

	bool bCanPaste = CanPaste();
	int pasteFlags = 0;
	if (!bCanPaste)
		pasteFlags |= MF_GRAYED;

	if (m_markedRecords.size() >= 2) // it makes sense when we have at least two items selected
	{
		int numMaterialsSelected = 0;
		for (int i = 0; i < m_markedRecords.size(); ++i)
		{
			if (m_markedRecords[i]->pMaterial)
				++numMaterialsSelected;
		}
		CString itemsSelected;
		itemsSelected.Format("  (%i Materials Selected)", numMaterialsSelected);
		menu.AppendMenu( MF_STRING|MF_GRAYED,0,itemsSelected );
		menu.AppendMenu( MF_SEPARATOR,0,"" );

		if (numMaterialsSelected >= 2) // ... and at least two materials
			menu.AppendMenu( MF_STRING,MENU_MERGE,"Merge" );
		else
			menu.AppendMenu( MF_STRING|MF_GRAYED,0,"Merge (Select two or more)" );
	}
	else if (!pRecord || pRecord->IsGroup()) // click on root, background or folder
	{
		menu.AppendMenu( MF_STRING|pasteFlags,MENU_PASTE,"Paste\tCtrl+V" );
		menu.AppendMenu( MF_SEPARATOR,0,"" );
		menu.AppendMenu( MF_STRING,MENU_ADDNEW,"Add New Material" );
		menu.AppendMenu( MF_STRING,MENU_ADDNEW_MULTI,"Add New Multi Material" );
#if !defined(IS_CRYDEV)
		menu.AppendMenu( MF_SEPARATOR,0,"" );
		menu.AppendMenu( MF_STRING|MF_GRAYED,0,"  Source Control" );
		menu.AppendMenu( MF_STRING,MENU_SCM_CHECK_OUT,"Check Out" );
		menu.AppendMenu( MF_STRING,MENU_SCM_CHECK_IN,"Check In" );
		menu.AppendMenu( MF_STRING,MENU_SCM_UNDO_CHECK_OUT,"Undo Check Out" );
		menu.AppendMenu( MF_STRING,MENU_SCM_GET_LATEST,"Get Latest Version" );
#endif 
	}
	else
	{
		if (pRecord->nSubMtlSlot >= 0)
		{
			int nFlags = 0;
			CMaterialBrowserRecord *pParentItemInfo = (CMaterialBrowserRecord*)pRecord->GetParentRecord();
			if (pParentItemInfo && pParentItemInfo->pMaterial != NULL)
			{
				uint32 nFileAttr = pParentItemInfo->pMaterial->GetFileAttributes();
				if (nFileAttr&SCC_FILE_ATTRIBUTE_READONLY)
					nFlags |= MF_GRAYED;
			}

			menu.AppendMenu( MF_STRING|nFlags,MENU_SUBMTL_MAKE,"Make Sub Material" );
			menu.AppendMenu( MF_STRING|nFlags,MENU_SUBMTL_CLEAR,"Clear Sub Material" );
			if (pMtl)
				menu.AppendMenu( MF_SEPARATOR,0,"" );
		}
		if (pMtl)
		{
			bool bDummMtl = false;
			if (!pMtl->IsPureChild() && (pMtl->GetFilename().IsEmpty() || pMtl->IsDummy()))
			{
				bDummMtl = true;
				menu.AppendMenu( MF_STRING,MENU_SAVE_TO_FILE,"Create Material From This" );
				menu.AppendMenu( MF_STRING,MENU_SAVE_TO_FILE_MULTI,"Create Multi Material From This" );
				menu.AppendMenu( MF_SEPARATOR,0,"" );
			}
			uint32 nFileAttr = pMtl->GetFileAttributes();

			if (pMtl->IsMultiSubMaterial())
			{
				menu.AppendMenu( MF_STRING,MENU_NUM_SUBMTL,"Set Number of Sub-Materials" );
				menu.AppendMenu( MF_SEPARATOR,0,"" );
			}
			menu.AppendMenu( MF_STRING,MENU_CUT,"Cut\tCtrl+X" );
			menu.AppendMenu( MF_STRING,MENU_COPY,"Copy\tCtrl+C" );
			menu.AppendMenu( MF_STRING|pasteFlags,MENU_PASTE,"Paste\tCtrl+V" );
			menu.AppendMenu( MF_STRING,MENU_COPY_NAME,"Copy Name to Clipboard" );
			menu.AppendMenu( MF_STRING | (nFileAttr&SCC_FILE_ATTRIBUTE_INPAK ? MF_GRAYED : 0),MENU_EXPLORE,"Explore" );
			menu.AppendMenu( MF_SEPARATOR,0,"" );
			menu.AppendMenu( MF_STRING,MENU_DUPLICATE,"Duplicate\tCtrl+D" ); 
			menu.AppendMenu( MF_STRING,MENU_RENAME,"Rename\tF2" );
			menu.AppendMenu( MF_STRING,MENU_DELETE,"Delete\tDel" );
			menu.AppendMenu( MF_STRING | (nFileAttr&(SCC_FILE_ATTRIBUTE_READONLY) ? 0 : MF_GRAYED), MENU_RESET, "Reset" );
			menu.AppendMenu( MF_SEPARATOR,0,"" );
			menu.AppendMenu( MF_STRING,MENU_ASSIGNTOSELECTION,"Assign to Selected Objects" );
			menu.AppendMenu( MF_STRING,MENU_SELECTASSIGNEDOBJECTS,"Select Assigned Objects" );
			menu.AppendMenu( MF_SEPARATOR,0,"" );
			menu.AppendMenu( MF_STRING,MENU_ADDNEW,"Add New Material" );
			menu.AppendMenu( MF_STRING,MENU_ADDNEW_MULTI,"Add New Multi Material" );
			menu.AppendMenu( MF_STRING|MF_GRAYED,0,"Merge (Select two or more)" );
#if !defined(IS_CRYDEV)
			if(gSettings.enableSourceControl)
			{
				menu.AppendMenu( MF_SEPARATOR,0,"" );

				if (nFileAttr & SCC_FILE_ATTRIBUTE_INPAK)
				{
					menu.AppendMenu( MF_STRING|MF_GRAYED,0,"  Material In Pak (Read Only)" );
				}
				else
				{
					menu.AppendMenu( MF_STRING|MF_GRAYED,0,"  Source Control" );
					if (!(nFileAttr&SCC_FILE_ATTRIBUTE_MANAGED))
					{
						menu.AppendMenu( MF_STRING,MENU_SCM_ADD,"Add To Source Control" );
					}
				}

				if (nFileAttr&SCC_FILE_ATTRIBUTE_MANAGED)
				{
					menu.AppendMenu( MF_STRING,MENU_SCM_GETPATH,"Copy Source Control Path To Clipboard" );
					menu.AppendMenu( MF_STRING | ((nFileAttr&SCC_FILE_ATTRIBUTE_READONLY || nFileAttr & SCC_FILE_ATTRIBUTE_INPAK) ? 0 : MF_GRAYED),MENU_SCM_CHECK_OUT,"Check Out" );
					menu.AppendMenu( MF_STRING | (nFileAttr&SCC_FILE_ATTRIBUTE_CHECKEDOUT ? 0 : MF_GRAYED),MENU_SCM_UNDO_CHECK_OUT,"Undo Check Out" );
					menu.AppendMenu( MF_STRING | (nFileAttr&SCC_FILE_ATTRIBUTE_CHECKEDOUT ? 0 : MF_GRAYED),MENU_SCM_CHECK_IN,"Check In" );
					menu.AppendMenu( MF_STRING,MENU_SCM_GET_LATEST,"Get Latest Version" );
					menu.AppendMenu( MF_STRING,MENU_SCM_HISTORY,"Show History" );
				}

				CString str;
				bool isHasTexs = GetTexturesFromMaterial(pMtl, str) != 0;
				menu.AppendMenu( MF_STRING | (isHasTexs ? 0 : MF_GRAYED),MENU_SCM_GET_LATEST_TEXTURES,"Get Textures" );
			}
#endif
		}
	}
	int cmd = menu.TrackPopupMenu( TPM_RETURNCMD|TPM_LEFTALIGN|TPM_LEFTBUTTON,point.x,point.y,this );
	switch(cmd)
	{
	case MENU_UNDEFINED: return; // do nothing
	case MENU_CUT: OnCut(); break;
	case MENU_COPY: OnCopy(); break;
	case MENU_COPY_NAME: OnCopyName(); break;
	case MENU_PASTE: OnPaste(); break;
	case MENU_EXPLORE: 
		{
			if(pMtl && pMtl->IsPureChild())
				pMtl = pMtl->GetParent();

			if (pMtl)
			{
				CString fullPath = pMtl->GetFilename();
				CString filename = PathUtil::GetFile(fullPath);
				if(int(ShellExecute(0, _T("open"), _T("explorer"), CString("/select, ")+filename, Path::GetPath(fullPath), SW_SHOWNORMAL))<=32)
					ShellExecute(0, _T("explore"), Path::GetPath(fullPath), 0, 0, SW_SHOWNORMAL);
			}
		}
		break;
	case MENU_DUPLICATE: OnDuplicate(); break;
	case MENU_RENAME: OnRenameItem(); break;
	case MENU_DELETE: DeleteItem(pRecord); break;
	case MENU_RESET: OnResetItem(); break;
	case MENU_ASSIGNTOSELECTION:
		GetIEditor()->ExecuteCommand("Material.AssignToSelection");
		break;
	case MENU_SELECTASSIGNEDOBJECTS:
		GetIEditor()->ExecuteCommand("Material.SelectAssignedObjects");
		break;
	case MENU_NUM_SUBMTL: OnSetSubMtlCount(pRecord); break;
	case MENU_ADDNEW: OnAddNewMaterial(); break;
	case MENU_ADDNEW_MULTI: OnAddNewMultiMaterial(); break;
	case MENU_MERGE: OnMergeMaterials(); break;

	case MENU_SUBMTL_MAKE: OnMakeSubMtlSlot(pRecord); break;
	case MENU_SUBMTL_CLEAR: OnClearSubMtlSlot(pRecord); break;

	case MENU_SCM_GETPATH: DoSourceControlOp(pRecord,ESCM_GETPATH); break;
	case MENU_SCM_ADD: DoSourceControlOp(pRecord,ESCM_IMPORT); break;
	case MENU_SCM_CHECK_OUT: DoSourceControlOp(pRecord,ESCM_CHECKOUT); break;
	case MENU_SCM_CHECK_IN: DoSourceControlOp(pRecord,ESCM_CHECKIN); break;
	case MENU_SCM_UNDO_CHECK_OUT: DoSourceControlOp(pRecord,ESCM_UNDO_CHECKOUT); break;
	case MENU_SCM_GET_LATEST: DoSourceControlOp(pRecord,ESCM_GETLATEST); break;
	case MENU_SCM_GET_LATEST_TEXTURES: DoSourceControlOp(pRecord, ESCM_GETLATESTTEXTURES); break;
	case MENU_SCM_HISTORY: DoSourceControlOp(pRecord, ESCM_HISTORY); break;

	case MENU_SAVE_TO_FILE: OnSaveToFile(false); break;
	case MENU_SAVE_TO_FILE_MULTI: OnSaveToFile(true); break;
		//case MENU_MAKE_SUBMTL: OnAddNewMultiMaterial(); break;
	}

	if (cmd != 0) // no need to refresh everything if we canceled menu
	{
		RefreshSelected();
		if (m_pListener)
			m_pListener->OnBrowserSelectItem(GetCurrentMaterial(),true);
	}
}

//////////////////////////////////////////////////////////////////////////
bool CMaterialBrowserCtrl::SelectItemByName( const CString materialName )
{
	uint32 materialNameCrc32 = MaterialNameToCrc32(materialName);
	for (Items::iterator it = m_items.begin(); it != m_items.end(); ++it)
	{
		CMaterialBrowserRecord *pRecord = *it;
		if (pRecord->materialNameCrc32 == materialNameCrc32)
		{
			SetSelectedItem( pRecord,0,false );
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserCtrl::PopulateItems()
{
	if (m_bIgnoreSelectionChange)
		return;

	if(m_bItemsValid)
		return;

	m_bItemsValid = true;
	m_bIgnoreSelectionChange = true;

	IDataBaseItem *pSelection = m_pMatMan->GetSelectedItem();

	m_tree.Populate();

	m_bIgnoreSelectionChange = false;

	if(pSelection)
	{
		SelectItem(pSelection);
		if (m_bHighlightMaterial)
		{
			m_bHighlightMaterial = false;
			m_pMatMan->SetHighlightedMaterial(0);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
uint32 CMaterialBrowserCtrl::MaterialNameToCrc32( const char *str )
{
	static Crc32Gen generator;
	return generator.GetCRC32Lowercase( str,strlen(str),-1 );
}

//////////////////////////////////////////////////////////////////////////
CMaterialBrowserRecord* CMaterialBrowserCtrl::GetSelectedRecord()
{
	CMaterialBrowserRecord *pFirstFoundRecord = 0;
	CMaterialBrowserRecord *pFocusedRecord = 0;
	int nCount = m_tree.GetSelectedRows()->GetCount();
	for (int i = 0; i < nCount; i++)
	{
		CXTPReportRow* pRow = m_tree.GetSelectedRows()->GetAt(i);
		CMaterialBrowserRecord *pRecord = m_tree.RowToRecord( pRow );
		if (pRecord)
		{
			if ( !pFirstFoundRecord )
				pFirstFoundRecord = pRecord;
			if ( pRow->IsFocused() )
				return pRecord;
		}
	}

	return pFirstFoundRecord;
}

//////////////////////////////////////////////////////////////////////////
CMaterial* CMaterialBrowserCtrl::GetCurrentMaterial()
{
	CMaterialBrowserRecord *pRecord = GetSelectedRecord();
	if (pRecord)
	{
		return pRecord->pMaterial;
	}
	else
	{
		return m_pMatMan->GetCurrentMaterial();
	}
}

//////////////////////////////////////////////////////////////////////////
CString CMaterialBrowserCtrl::GetSelectedMaterialID()
{
	CMaterialBrowserRecord *pRecord = GetSelectedRecord();
	if (pRecord)
	{
		return pRecord->materialName;
	}
	return "";
}



//////////////////////////////////////////////////////////////////////////
// CMaterialBrowserTreeCtrl

//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CMaterialBrowserTreeCtrl, CTreeCtrlReport)
	ON_NOTIFY_REFLECT(NM_RCLICK, OnNMRclick)
	ON_NOTIFY_REFLECT(NM_KEYDOWN, OnTreeKeyDown )
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////////
CMaterialBrowserTreeCtrl::CMaterialBrowserTreeCtrl()
{
	CMFCUtils::LoadTrueColorImageList( m_imageList,IDB_MATERIAL_TREE,20,RGB(255,0,255) );
	CMFCUtils::LoadTrueColorImageList( m_imageList,IDB_FILE_STATUS,20,RGB(255,0,255) );

	//m_imageList.SetOverlayImage( ITEM_IMAGE_OVERLAY_CGF,ITEM_OVERLAY_ID_CGF );
	//m_imageList.SetOverlayImage( ITEM_IMAGE_OVERLAY_INPAK,ITEM_OVERLAY_ID_INPAK );
	//m_imageList.SetOverlayImage( ITEM_IMAGE_OVERLAY_READONLY,ITEM_OVERLAY_ID_READONLY );
	//m_imageList.SetOverlayImage( ITEM_IMAGE_OVERLAY_ONDISK,ITEM_OVERLAY_ID_ONDISK );
	//m_imageList.SetOverlayImage( ITEM_IMAGE_OVERLAY_LOCKED,ITEM_OVERLAY_ID_LOCKED );
	//m_imageList.SetOverlayImage( ITEM_IMAGE_OVERLAY_CHECKEDOUT,ITEM_OVERLAY_ID_CHECKEDOUT );
	//m_imageList.SetOverlayImage( ITEM_IMAGE_OVERLAY_NO_CHECKOUT,ITEM_OVERLAY_ID_NO_CHECKOUT );

	SetMultipleSelection( TRUE );
	SetImageList( &m_imageList );

	CXTPReportColumn *pCol1 = AddTreeColumn( "Material" );
	pCol1->SetSortable(TRUE);
	GetColumns()->SetSortColumn(pCol1,TRUE);
	SetRowsCompareFunc( RowCompareFunc );
	SetExpandOnDblClick(true);

	m_pMtlBrowser = 0;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserTreeCtrl::OnFillItems()
{
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserTreeCtrl::OnSelectionChanged()
{
	CMaterialBrowserRecord *pRecord = 0;

	TMaterialBrowserRecords markedRecords;

	int nCount = GetSelectedRows()->GetCount();
	for (int i = 0; i < nCount; i++)
	{
		CXTPReportRow *pRow = GetSelectedRows()->GetAt(i);
		CMaterialBrowserRecord *pCurrentRecord = RowToRecord( pRow );
		if (pCurrentRecord)
		{
			markedRecords.push_back( pCurrentRecord );
			if (pRow->IsFocused())
				pRecord = pCurrentRecord;
		}
	}

	if (!pRecord && !markedRecords.empty() )
		pRecord = markedRecords.front();

	m_pMtlBrowser->SetSelectedItem( pRecord,&markedRecords,true );
}

void CMaterialBrowserTreeCtrl::DeleteAllItems()
{
	__super::DeleteAllItems();
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserTreeCtrl::OnNMRclick( NMHDR *pNMHDR, LRESULT *pResult )
{
	CPoint point;
	// Find node under mouse.	
	GetCursorPos( &point );
	ScreenToClient( &point );
	// Select the item that is at the point myPoint.

	CMaterialBrowserRecord *pRecord = 0;

	CXTPReportRow* pRow = HitTest(point);
	if (pRow)
	{
		pRecord = (CMaterialBrowserRecord*)pRow->GetRecord();
	}

	if (pRecord && GetSelectedCount() <= 1)
	{
		m_pMtlBrowser->SetSelectedItem( pRecord,0,false );
	}

	// Show helper menu.
	GetCursorPos( &point );
	m_pMtlBrowser->ShowContextMenu( pRecord,point );
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserTreeCtrl::OnTreeKeyDown(NMHDR* pNMHDR, LRESULT* pResult)
{
	GetAsyncKeyState(VK_CONTROL);
	bool bCtrl = GetAsyncKeyState(VK_CONTROL) != 0;
	// Key press in items tree view.
	NMTVKEYDOWN *nm = (NMTVKEYDOWN*)pNMHDR;

	CPoint point;
	// Find node under mouse.
	GetCursorPos( &point );
	ScreenToClient( &point );

	CMaterialBrowserRecord *pRecord = 0;
	CXTPReportRow* pRow = HitTest(point);
	if (pRow)
	{
		pRecord = (CMaterialBrowserRecord*)pRow->GetRecord();
	}

	if (bCtrl && (nm->wVKey == 'c' || nm->wVKey == 'C'))
	{
		m_pMtlBrowser->OnCopy();	// Ctrl+C
	}
	if (bCtrl && (nm->wVKey == 'v' || nm->wVKey == 'V'))
	{
		m_pMtlBrowser->OnPaste(); // Ctrl+V
	}
	if (bCtrl && (nm->wVKey == 'x' || nm->wVKey == 'X'))
	{
		m_pMtlBrowser->OnCut(); // Ctrl+X
	}
	if (bCtrl && (nm->wVKey == 'd' || nm->wVKey == 'D'))
	{
		m_pMtlBrowser->OnDuplicate(); // Ctrl+D
	}
	if (nm->wVKey == VK_DELETE)
	{
		if(pRecord)
			m_pMtlBrowser->DeleteItem(pRecord);
	}
	if (nm->wVKey == VK_F2)
	{
		m_pMtlBrowser->OnRenameItem();
	}
	if (nm->wVKey == VK_INSERT)
	{
		m_pMtlBrowser->OnAddNewMaterial();
	}
}


//////////////////////////////////////////////////////////////////////////
CTreeItemRecord* CMaterialBrowserTreeCtrl::CreateGroupRecord( const CString &name,int nGroupIcon )
{
	CRecord *pRec = new CRecord;
	pRec->CreateStdItems();
	pRec->SetName(name);
	pRec->SetGroup(true);
	pRec->SetIcon(nGroupIcon);
	return pRec;
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserTreeCtrl::SelectItem( CMaterialBrowserRecord *pRecord, const TMaterialBrowserRecords *pMarkedRecords )
{
	EnsureItemVisible( pRecord );

	GetSelectedRows()->Clear();
	if (pRecord || pMarkedRecords)
	{
		CXTPReportRows *pRows = GetRows();
		for (int i = 0,nNumRows = pRows->GetCount(); i < nNumRows; i++)
		{
			CXTPReportRow *pRow = pRows->GetAt(i);
			CMaterialBrowserRecord* pCurrentRecord = RowToRecord(pRow);
			if ( pCurrentRecord == pRecord )
			{
				pRow->SetSelected(TRUE);
				SetFocusedRow(pRow);
			}
			else if ( pMarkedRecords && 
				std::find( pMarkedRecords->begin(),pMarkedRecords->end(),pCurrentRecord ) != pMarkedRecords->end() )
			{
				pRow->SetSelected(TRUE);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserTreeCtrl::UpdateItemState( CMaterialBrowserRecord *pRecord )
{
	if(!pRecord)
		return;

	uint32 attr = 0;
	bool bFromCGF = false;
	if (pRecord->pMaterial != NULL)
	{
		attr = pRecord->pMaterial->GetFileAttributes();
		bFromCGF = pRecord->pMaterial->IsFromEngine();
	}

	int nIconMtl = ITEM_IMAGE_MATERIAL;
	if (pRecord->pMaterial && pRecord->pMaterial->IsMultiSubMaterial())
	{
		nIconMtl = ITEM_IMAGE_MULTI_MATERIAL;
	}
	else if (pRecord->nSubMtlSlot >= 0 && pRecord->pMaterial && !pRecord->pMaterial->IsPureChild())
	{
		nIconMtl = ITEM_IMAGE_SHARED_MATERIAL;
	}
	pRecord->SetIcon(nIconMtl);

	int nIcon = -1;
	if (bFromCGF)
	{
		// From CGF.
		nIcon = ITEM_IMAGE_OVERLAY_CGF;
	}
	else if (attr&SCC_FILE_ATTRIBUTE_INPAK)
	{
		// Inside pak file.
		nIcon = ITEM_IMAGE_OVERLAY_INPAK;
	}
	else if (attr&SCC_FILE_ATTRIBUTE_CHECKEDOUT) 
	{
		// Checked out.
		nIcon = ITEM_IMAGE_OVERLAY_CHECKEDOUT;
	}
	else if (attr&SCC_FILE_ATTRIBUTE_MANAGED)
	{
		// Checked out.
		nIcon = ITEM_IMAGE_OVERLAY_LOCKED;
	}
	else if (attr&SCC_FILE_ATTRIBUTE_READONLY)
	{
		// Read-Only.
		nIcon = ITEM_IMAGE_OVERLAY_READONLY;
	}
	pRecord->SetIcon2( nIcon );
}


//////////////////////////////////////////////////////////////////////////
int _cdecl CMaterialBrowserTreeCtrl::RowCompareFunc( const CXTPReportRow** pRow1, const CXTPReportRow** pRow2 )
{
	CRecord *pRec1 = RowToRecord(*pRow1);
	CRecord *pRec2 = RowToRecord(*pRow2);

	bool bGroup1 = pRec1->IsGroup();
	bool bGroup2 = pRec2->IsGroup();

	if (bGroup1 && !bGroup2)
		return -1;
	if (!bGroup1 && bGroup2)
		return 1;

	if( pRec1->nSubMtlSlot > -1 && pRec2->nSubMtlSlot > -1 )
		return (pRec1->nSubMtlSlot > pRec2->nSubMtlSlot) ? 1 : -1;

	return stricmp(pRec1->GetName(),pRec2->GetName());
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserTreeCtrl::OnItemExpanded( CXTPReportRow *pRow,bool bExpanded )
{
	CMaterialBrowserRecord* pRecord = (CMaterialBrowserRecord*)pRow->GetRecord();
	if(pRecord && bExpanded)
		pRecord->UpdateChildItems();
}


//////////////////////////////////////////////////////////////////////////
// CMaterialBrowserRecord

//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserRecord::CreateItems()
{
	int nIcon = ITEM_IMAGE_MATERIAL;
	if (pMaterial && pMaterial->IsMultiSubMaterial())
	{
		nIcon = ITEM_IMAGE_MULTI_MATERIAL;
	}
	else if (nSubMtlSlot >= 0 && pMaterial && !pMaterial->IsPureChild())
	{
		nIcon = ITEM_IMAGE_SHARED_MATERIAL;
	}

	CString itemText;

	if (nSubMtlSlot >= 0)
	{
		int numSubMaterials = 0;
		if ( pMaterial && pMaterial->GetParent() )
			numSubMaterials = pMaterial->GetParent()->GetSubMaterialCount() ;
		const char* format = numSubMaterials > 9 ? "[%02d] %s" : "[%d] %s";
		itemText.Format( format,nSubMtlSlot+1,(const char*)materialName );
	}
	else
	{
		itemText = materialName;
	}

	SetName(itemText);
	CreateStdItems();
	SetIcon(nIcon);
}


//////////////////////////////////////////////////////////////////////////
void CMaterialBrowserRecord::UpdateChildItems()
{
	for(int i = GetChildCount()-1; i>=0; --i)
	{
		((CMaterialBrowserRecord*)GetChild(i))->bUpdated=false;
	}
}


//////////////////////////////////////////////////////////////////////////
CString CMaterialBrowserRecord::CollectChildItems()
{
	CString paths;
	for(int i = GetChildCount()-1; i>=0; --i)
	{
		CMaterialBrowserRecord* pRecord = (CMaterialBrowserRecord*)GetChild(i);
		if(pRecord && pRecord->pMaterial)
		{
			paths += pRecord->pMaterial->GetFilename();
			if( i )
				paths += ";";
		}
	}
	return paths;
}
